-- Copyright 2008 Jesse E. H-K
--
-- This file is part of Icepac.
--
-- Icepac is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- Icepac is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with Icepac.  If not, see <http://www.gnu.org/licenses/>.

require "libicepac"

-- BEGIN LOCAL FUNCTIONS --

-- Check if a table is empty.
local function empty(t)
   return t[1] == nil
end

-- Returns true when all the values in a table
-- are true.
local function all(t)
   for repo, found in pairs(t) do
      if not found then
         return false
      end
   end
   
   return true
end

-- Given a series of path "parts",
-- combine them with a path seperator.
local function buildpath(...)
   local result = {}
   for i = 1, arg.n do
      table.insert(result, arg[i])
   end
   
   return table.concat(result, "/")
end

local function exists(value, table)
   for k, v in pairs(table) do
      if v == value then
         return true
      end
   end
   
   return false
end

-- Searches for a file with a name matching
-- "file" in the directory "dir".
local function fileexists(file, dir)
   for ent in assert(icepac.iterdir(dir)) do
      if icepac.basename(ent) == file then
         return true
      end
   end
   
   return false
end

-- Escapes package names so that they can
-- be searched for correctly using patterns.
local function escape(str)
   return string.gsub(str, "-", "%%-")
end

-- Given a filepath, this function
-- will create the directory tree if it doesn't exist.
local function mkdirtree(filepath)
   local parts = { "/" }
   local cpath

   for part in string.gmatch(filepath, "[^\/]+") do
      table.insert(parts, part)
      cpath = table.concat(parts, "/")
      if not icepac.isdir(cpath) then
         assert(icepac.mkdir(cpath))
      end
   end
end

-- END LOCAL FUNCTIONS --

-- BEGIN EXPORTED FUNCTIONS --

-- Returns true if all repository entries specified in REPOS
-- are present in the specified ABS_ROOT
function icepac.checktree()
   local found = {}
   for _, repo in ipairs(REPOS) do
      found[repo] = false
   end
   
   for ent in assert(icepac.iterdir(ABS_ROOT)) do
      for _, repo in ipairs(REPOS) do
         if icepac.basename(ent) == repo then
            found[repo] = true
         end
      end
   end
   
   return all(found)
end

-- This function examines the contents of the given "dirname"
-- and determines if it is a package directory (that is,
-- determines if a PKGBUILD file is present).
function icepac.pkgdir(dirname)
   for ent in assert(icepac.iterdir(dirname)) do
      if icepac.basename(ent) == "PKGBUILD" then
         return true
      end
   end
   
   return false
end

-- Locate an package in the ABS tree
-- with an exact match to "name".
-- If a package is found, this function returns
-- the location of the package in the filesystem.
-- Otherwise, it returns nil.
function icepac.locate(name)
   local function find_r(current)
      local contents = assert(icepac.listdir(current))
      for _, ent in ipairs(contents) do
         if icepac.isdir(ent) then
            local base = icepac.basename(ent)
            if icepac.pkgdir(ent) then
               if base == name then
                  return ent
               end
            else
               local result = find_r(buildpath(current, base))

               if result then
                  return result
               end
            end
         end
      end

      return nil
   end
   
   for repo in assert(icepac.iterdir(ABS_ROOT)) do
      if exists(icepac.basename(repo), REPOS) then
         local found = find_r(repo)

         if found then
            return found
         end
      end
   end

   return nil
end

-- Returns a table containing the paths to all
-- the packages matching the string "desired".
function icepac.search(desired)
   local foundpaths = {}
   local escapedpat = escape(desired)
   
   local function find_r(current)
      
      -- The use of icepac.listdir (which returns a table)
      -- over icepac.iterdir (which is an iterator) is important here.
      -- With the iterator, the system runs out of file handles
      -- due to the nature of this algorithm.
      local contents = assert(icepac.listdir(current))
      for _, ent in ipairs(contents) do
         if icepac.isdir(ent) then
            local b = icepac.basename(ent)
            
            if not string.find(LOCAL_DIR, b, 1, true) then
               if icepac.pkgdir(ent) and string.match(b, escapedpat) then
                  table.insert(foundpaths, ent)
               else
                  find_r(buildpath(current, b))
               end
            end
         end
      end
   end
   
   for repo in assert(icepac.iterdir(ABS_ROOT)) do
      if exists(icepac.basename(repo), REPOS) then
         find_r(repo)
      end
   end
   
   if empty(foundpaths) then
      return nil
   else
      return foundpaths
   end
end

-- Copy a package with "name" and "loc" (location in the filesystem)
-- to the specified local directory. Will create the local
-- directory (and its hierarchy) if it doesn't already
-- exist.
function icepac.cppackage(name, loc)
   local floc = LOCAL_DIR .. "/" .. name
   
   if not icepac.isdir(LOCAL_DIR) then
      mkdirtree(LOCAL_DIR)
   end
   
   if not icepac.isdir(floc) then
      assert(icepac.mkdir(floc))
   end
   
   for ent in assert(icepac.iterdir(loc)) do  
      local base = icepac.basename(ent)
      assert(icepac.cpfile(ent, floc .. "/" .. base))
   end
   
   return floc
end

-- END EXPORTED FUNCTIONS --